#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/list.h>
#include <asm/uaccess.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include "Definitii.h"

void add_a_rule(struct regula_firewall_bruta* a_rule_desp) 
{
    struct regula_firewall* a_rule;
    a_rule = kmalloc(sizeof(*a_rule), GFP_KERNEL);
    if (a_rule == NULL) 
	{
        printk(KERN_INFO "eroare: nu poate aloca memorie pentru o noua regula\n");
        return;
    }
    a_rule->in_out = a_rule_desp->in_out;
    if (strcmp(a_rule_desp->src_ip, "-") != 0) 
        a_rule->src_ip = ip_str_to_hl(a_rule_desp->src_ip);
    else
        a_rule->src_ip = NULL;
    if (strcmp(a_rule_desp->src_netmask, "-") != 0)
        a_rule->src_netmask = ip_str_to_hl(a_rule_desp->src_netmask);
    else
        a_rule->src_netmask = NULL;
    if (strcmp(a_rule_desp->src_port, "-") != 0)
        a_rule->src_port = port_str_to_int(a_rule_desp->src_port);
    else 
        a_rule->src_port = NULL;
    if (strcmp(a_rule_desp->dest_ip, "-") != 0)
        a_rule->dest_ip = ip_str_to_hl(a_rule_desp->dest_ip);
    else 
        a_rule->dest_ip = NULL;
    if (strcmp(a_rule_desp->dest_netmask, "-") != 0)
	{
		a_rule->dest_netmask = ip_str_to_hl(a_rule_desp->dest_netmask);
	}
    else 
	{
		a_rule->dest_netmask = NULL;
	}
    if (strcmp(a_rule_desp->dest_port, "-") != 0)
        a_rule->dest_port = port_str_to_int(a_rule_desp->dest_port);
    else 
        a_rule->dest_port = NULL;
    a_rule->proto = a_rule_desp->proto;
    a_rule->action = a_rule_desp->action;
    printk(KERN_INFO "add_a_rule: in_out=%u, src_ip=%u, src_netmask=%u, src_port=%u, dest_ip=%u, dest_netmask=%u, dest_port=%u, proto=%u, action=%u\n", a_rule->in_out, a_rule->src_ip, a_rule->src_netmask, a_rule->src_port, a_rule->dest_ip, a_rule->dest_netmask, a_rule->dest_port, a_rule->proto, a_rule->action);
    INIT_LIST_HEAD(&(a_rule->list));
    list_add_tail(&(a_rule->list), &(regula.list));
}


void init_regula_firewall_bruta(struct regula_firewall_bruta* a_rule_desp) 
{
    a_rule_desp->in_out = 0;
    a_rule_desp->src_ip = (char *)kmalloc(16, GFP_KERNEL);
    a_rule_desp->src_netmask = (char *)kmalloc(16, GFP_KERNEL);
    a_rule_desp->src_port = (char *)kmalloc(16, GFP_KERNEL);
    a_rule_desp->dest_ip = (char *)kmalloc(16, GFP_KERNEL);
    a_rule_desp->dest_netmask = (char *)kmalloc(16, GFP_KERNEL);
    a_rule_desp->dest_port = (char *)kmalloc(16, GFP_KERNEL);
    a_rule_desp->proto = 0;
    a_rule_desp->action = 0;
}

void delete_a_rule(int num) 
{
    int i = 0;
    struct list_head *p, *q;
    struct regula_firewall *a_rule;
    printk(KERN_INFO "sterge o regula: %d\n", num);
    list_for_each_safe(p, q, &regula.list) 
	{
        ++i;
        if (i == num) 
		{
            a_rule = list_entry(p, struct regula_firewall, list);
            list_del(p);
            kfree(a_rule);
            return;
        }
    }
}

int procf_read(char *buffer, char **buffer_location, off_t offset, int buffer_length, int *eof, void *data)
{
	int ret;
	struct regula_firewall *a_rule;
	char token[20];
	printk(KERN_INFO "s-a apelat procf_read  \n", PROCF_NAME);
    if (offset > 0)
	{
        printk(KERN_INFO "nu este nimic de citit\n");
        *eof = 1;
        return 0;
    }
	else 
	{
		procf_buffer_pos = 0;
        ret = 0;
        list_for_each_entry(a_rule, &regula.list, list) 
		{
            if (a_rule->in_out==1) 
			{
                strcpy(token, "in");
            } 
			else 
				if (a_rule->in_out==2) 
				{
					strcpy(token, "out");
				}
            printk(KERN_INFO "token: %s\n", token);
            memcpy(procf_buffer + procf_buffer_pos, token, strlen(token));
            procf_buffer_pos += strlen(token);
            memcpy(procf_buffer + procf_buffer_pos, " ", 1);
            procf_buffer_pos++;

            //src ip
            if (a_rule->src_ip == NULL) 
			{
                strcpy(token, "-");
            } 
			else
			{
                ip_hl_to_str(a_rule->src_ip, token);
            } 
            printk(KERN_INFO "token: %s\n", token);
            memcpy(procf_buffer + procf_buffer_pos, token, strlen(token));
            procf_buffer_pos += strlen(token);
            memcpy(procf_buffer + procf_buffer_pos, " ", 1);
            procf_buffer_pos++;

            //src netmask
            if (a_rule->src_netmask==NULL) 
			{
                strcpy(token, "-");
            } 
			else 
			{
                ip_hl_to_str(a_rule->src_netmask, token);
            } 
            printk(KERN_INFO "token: %s\n", token);
            memcpy(procf_buffer + procf_buffer_pos, token, strlen(token));
            procf_buffer_pos += strlen(token);
            memcpy(procf_buffer + procf_buffer_pos, " ", 1);
            procf_buffer_pos++;

            //src port
            if (a_rule->src_port==0) 
			{
                strcpy(token, "-");
            } 
			else 
			{
                port_int_to_str(a_rule->src_port, token);
            } 
            printk(KERN_INFO "token: %s\n", token);
            memcpy(procf_buffer + procf_buffer_pos, token, strlen(token));
            procf_buffer_pos += strlen(token);
            memcpy(procf_buffer + procf_buffer_pos, " ", 1);
            procf_buffer_pos++;

            //dest ip
            if (a_rule->dest_ip==NULL) 
			{
                strcpy(token, "-");
            }
			else 
			{
                ip_hl_to_str(a_rule->dest_ip, token);
            } 
            printk(KERN_INFO "token: %s\n", token);
            memcpy(procf_buffer + procf_buffer_pos, token, strlen(token));
            procf_buffer_pos += strlen(token);
            memcpy(procf_buffer + procf_buffer_pos, " ", 1);
            procf_buffer_pos++;
            //dest netmask
            if (a_rule->dest_netmask==NULL) 
			{
                strcpy(token, "-");
            }
			else 
			{
                ip_hl_to_str(a_rule->dest_netmask, token);
            } 
            printk(KERN_INFO "token: %s\n", token);
            memcpy(procf_buffer + procf_buffer_pos, token, strlen(token));
            procf_buffer_pos += strlen(token);
            memcpy(procf_buffer + procf_buffer_pos, " ", 1);
            procf_buffer_pos++;
            //dest port
            if (a_rule->dest_port==0) 
			{
                strcpy(token, "-");
            } 
			else 
			{
                port_int_to_str(a_rule->dest_port, token);
            } 
            printk(KERN_INFO "token: %s\n", token);
            memcpy(procf_buffer + procf_buffer_pos, token, strlen(token));
            procf_buffer_pos += strlen(token);
            memcpy(procf_buffer + procf_buffer_pos, " ", 1);
            procf_buffer_pos++;
            //protocol
            if (a_rule->proto==0) 
			{
                strcpy(token, "ALL");
            } 
			else
				if (a_rule->proto==1) 
				{
					strcpy(token, "TCP");
				}  
				else 
					if (a_rule->proto==2) 
					{
						strcpy(token, "UDP");
					}
            printk(KERN_INFO "token: %s\n", token);
            memcpy(procf_buffer + procf_buffer_pos, token, strlen(token));
            procf_buffer_pos += strlen(token);
            memcpy(procf_buffer + procf_buffer_pos, " ", 1);
            procf_buffer_pos++;
            //action
            if (a_rule->action==0)
			{
                strcpy(token, "BLOCK");
            } 
			else 
				if (a_rule->action==1)
				{
					strcpy(token, "UNBLOCK");
				}
            printk(KERN_INFO "token: %s\n", token);
            memcpy(procf_buffer + procf_buffer_pos, token, strlen(token));
            procf_buffer_pos += strlen(token);
            memcpy(procf_buffer + procf_buffer_pos, "\n", 1);
            procf_buffer_pos++;
        }
	 memcpy(buffer, procf_buffer, procf_buffer_pos);
     ret = procf_buffer_pos;
	return ret; 	
	}
	



int procf_write(struct file *file, const char *buffer, unsigned long count, void *data)
{
   int i, j;
   struct regula_firewall_bruta *rule_desp;
   printk(KERN_INFO "s-a apelat procf_write.\n");

   /*citest continutul in buffer*/
   procf_buffer_pos = 0;

   printk(KERN_INFO "pos: %ld; count: %ld\n", procf_buffer_pos, count);

   if (procf_buffer_pos + count > PROCF_MAX_SIZE) 
   {
       count = PROCF_MAX_SIZE-procf_buffer_pos;
   } 

   if (copy_from_user(procf_buffer+procf_buffer_pos, buffer, count)) 
   {
       return -EFAULT;
   }
   if (procf_buffer[procf_buffer_pos] == 'p') 
   {
       //print command
       return 0;
   }
   else 
	   if (procf_buffer[procf_buffer_pos] == 'd') 
	   {
		   //delete command
		   i = procf_buffer_pos+1; j = 0;
		   while ((procf_buffer[i]!=' ') && (procf_buffer[i]!='\n') ) 
		   {
			   printk(KERN_INFO "delete: %d\n", procf_buffer[i]-'0');
			   j = j*10 + (procf_buffer[i]-'0');
			   ++i;
		   }
		   printk(KERN_INFO "sterge o regula: %d\n", j);
		   delete_a_rule(j);
		   return count;
	   }

   /*adauga o noua regula*/
   rule_desp = kmalloc(sizeof(*rule_desp), GFP_KERNEL);
   if (rule_desp == NULL) 
   {
       printk(KERN_INFO "eroare la alocarea dinamica\n");
       return -ENOMEM;
   } 

   init_regula_firewall_bruta(rule_desp);

   /**completeaza campurile noii reguli **/
   /***in_out***/
   i = procf_buffer_pos; j = 0;
   if (procf_buffer[i]!=' ') 
   {
       rule_desp->in_out = (unsigned char)(procf_buffer[i++] - '0');
   }
   ++i;
   printk(KERN_INFO "in or out: %u\n", rule_desp->in_out);
   /***src ip***/
   j = 0;
   while (procf_buffer[i]!=' ') 
   {
       rule_desp->src_ip[j++] = procf_buffer[i++];
   }
   ++i;
   rule_desp->src_ip[j] = '\0';
   printk(KERN_INFO "src ip: %s\n", rule_desp->src_ip);
   /***src netmask***/
   j = 0;
   while (procf_buffer[i]!=' ') 
   {
       rule_desp->src_netmask[j++] = procf_buffer[i++];
   }
   ++i;
   rule_desp->src_netmask[j] = '\0';
   printk(KERN_INFO "src netmask: %s\n", rule_desp->src_netmask);
   /***src port number***/
   j = 0;
   while (procf_buffer[i]!=' ') 
   {
       rule_desp->src_port[j++] = procf_buffer[i++];
   }
   ++i;
   rule_desp->src_port[j] = '\0';
   printk(KERN_INFO "src_port: %s\n", rule_desp->src_port);
   /***dest ip***/
   j = 0;
   while (procf_buffer[i]!=' ') 
   {
       rule_desp->dest_ip[j++] = procf_buffer[i++];
   }
   ++i;
   rule_desp->dest_ip[j] = '\0';
   printk(KERN_INFO "dest ip: %s\n", rule_desp->dest_ip);
   /***dest netmask***/
   j = 0;
   while (procf_buffer[i]!=' ') 
   {
       rule_desp->dest_netmask[j++] = procf_buffer[i++];
   }
   ++i;
   rule_desp->dest_netmask[j] = '\0';
   printk(KERN_INFO "dest netmask%s\n", rule_desp->dest_netmask);
   /***dest port***/
   j = 0;
   while (procf_buffer[i]!=' ') 
   {
       rule_desp->dest_port[j++] = procf_buffer[i++];
   }
   ++i;
   rule_desp->dest_port[j] = '\0';
   printk(KERN_INFO "dest port: %s\n", rule_desp->dest_port);
   /***proto***/
   j = 0;
   if (procf_buffer[i]!=' ') 
   {
       if (procf_buffer[i] != '-')
           rule_desp->proto = (unsigned char)(procf_buffer[i++]-'0');
       else
           ++i;
   }
   ++i;

   printk(KERN_INFO "proto: %d\n", rule_desp->proto);
   /***action***/
   j = 0;
   if (procf_buffer[i]!=' ') 
   {
       if (procf_buffer[i] != '-')
           rule_desp->action = (unsigned char)(procf_buffer[i++]-'0');
       else
           ++i;
   ++i;
   printk(KERN_INFO "action: %d\n", rule_desp->action);
   add_a_rule(rule_desp);
   kfree(rule_desp);
   printk(KERN_INFO "--------------------\n");
   return count;
}
